package mail.fetch.netease;

import mail.entity.ProxyEntity;
import mail.exception.*;
import mail.util.PublicMethod;
import mail.util.CookieParser;
import mail.util.HttpResponseUtil;
import mail.util.RegexSearcher;
import mail.util.StatusCode;

import java.io.IOException;
import java.net.CookieManager;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static com.han.http.util.Print.printlnErr;
import static com.han.http.util.Print.printlnInfo;

/**
 * @author: Hanjiafeng
 * @date: Created in 14:21 2019/3/8
 * @desc: 收件箱
 */
class Inbox11 {

    /**
     * 定义请求发送对象
     */
    private HttpClient httpClient;
    /**
     * 定义请求Builder
     */
    private HttpRequest.Builder baseRequestBuilder;
    /**
     * 登录传入的CookieManager
     */
    private CookieManager cookieManager;
    /**
     * 当前邮箱类型
     */
    private MailType mailType;
    /**
     * 默认第一封邮件发送事件在半年内
     */
    private boolean isDuringHalfYear = true;
    /**
     * 单页邮件数量
     */
    private int signalPageLimit;

    /**
     * 使用CookieManager和ProxyEntity初始化收件类
     * @param cookieManager cm
     * @param proxyEntity pe
     */
    Inbox11(CookieManager cookieManager, ProxyEntity proxyEntity) {

        this.cookieManager = cookieManager;

        HttpClient.Builder clientBuilder = HttpClient.newBuilder()
                .connectTimeout(Duration.ofMinutes(1))
                .cookieHandler(cookieManager)
                //禁止自动跳转
                .followRedirects(HttpClient.Redirect.NEVER);
        //如果代理不为null,设置代理
        if (!Objects.isNull(proxyEntity)) {
            clientBuilder.proxy(ProxySelector.of(new InetSocketAddress(proxyEntity.getIp(), proxyEntity.getPort())));
        }
        //设置通用参数
        httpClient = clientBuilder
                .build();
        //设置通用参数
        baseRequestBuilder = HttpRequest.newBuilder()
                .timeout(Duration.ofMinutes(1))
                .setHeader("Accept-Encoding", "gzip, deflate, br")
                .setHeader("Accept-Language", "zh-CN,zh;q=0.9")
                .setHeader("DNT", "1")
                .setHeader("Upgrade-Insecure-Requests", "1")
                .setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3664.3 Safari/537.36");
    }

    /**
     * 获取半年内的信用卡账单邮件
     *
     * @param mailType 邮件类型
     * @return 半年内的邮件列表
     * @throws MailTypeException 邮件类型错误
     */
    List<NetEasyMailEntity> getMailListDuringHalfYear(MailType mailType) throws MailTypeException, InitException {

        //设置邮件类型
        this.mailType = mailType;
        if (MailType.isNotNetEase(mailType)) {
            throw new MailTypeException("邮箱类型错误,仅可为网易邮箱");
        }

        //获取sid,并访问邮箱主页
        String coreMailStr = CookieParser.getSpecifiedCookie(cookieManager, "Coremail");
        String sid = coreMailStr.substring(coreMailStr.indexOf("%") + 1, coreMailStr.lastIndexOf("%"));
        mailMainPage(sid);

        List<NetEasyMailEntity> duringHalfYearMailList = new ArrayList<>();
        //单个页面邮件数
        signalPageLimit = 20;
        int currentPageIndex = 0;
        boolean isLastPage = false;
        while (isDuringHalfYear && !isLastPage) {
            try {
                String mailListPage = getMailListByPageIndex(sid, currentPageIndex);
                List<NetEasyMailEntity> tempList = parseMailListPage(mailListPage);
                isLastPage = isLastPage(mailListPage, currentPageIndex);
                duringHalfYearMailList.addAll(tempList);
                //如果最后一封邮件也在180天之内并且不是最后一页,页码+1,继续获取下一页的邮件,重复判断
                if (isDuringHalfYear && !isLastPage) {
                    currentPageIndex++;
                }
            } catch (ContentMissedException e) {
                e.printStackTrace();
            }
            try {
                //休眠3秒,避免被封
                Thread.sleep(1000 * 3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return duringHalfYearMailList;
    }

    /**
     * 检测当前页面是不是最后一页
     *
     * @param mailListPage     邮件页面
     * @param currentPageIndex 当前页码
     * @return 是否是最后一页
     */
    private boolean isLastPage(String mailListPage, int currentPageIndex) {
        boolean isLastPage = false;
        String totalCountStr = RegexSearcher.matchFirst(mailListPage, "(?<='total':)[\\d]{1,9}(?=})");
        int totalCount;
        try {
            totalCount = Integer.parseInt(totalCountStr);
        } catch (NumberFormatException e) {
            //如果不包含'id',默认为最后一页
            isLastPage = !mailListPage.contains("'id'");
            return isLastPage;
        }
        //总页数,0:第一页,1:第二页
        int pageTotal = totalCount / signalPageLimit;
        //如果当前页数大于等于总页数,当前页为最后一页
        if (currentPageIndex >= pageTotal) {
            isLastPage = true;
        }

        return isLastPage;
    }

    /**
     * 访问邮箱主页(不然无法获取邮件内容)
     *
     * @param sid sid
     * @throws InitException 初始化失败
     */
    private void mailMainPage(String sid) throws InitException {
        String uri163 = "https://mail.163.com/js6/main.jsp?sid=" + sid + "&df=mail163_letter";
        String uri126 = "https://mail.126.com/js6/main.jsp?sid=" + sid + "&df=mail126_letter";
        String uriYeah = "https://mail.yeah.net/js6/main.jsp?sid=" + sid + "&df=webmailyeah";
        String uri = MailSwitcher.switchParameter(mailType, uri163, uri126, uriYeah);
        HttpRequest httpRequest = baseRequestBuilder
                .copy()
                .uri(URI.create(uri))
                .build();
        try {
            httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray());
        } catch (IOException | InterruptedException e) {
            throw new InitException("初始化收件箱主页失败:" + e.getMessage());
        }
    }

    /**
     * 获取指定页码的邮件页面
     *
     * @param pageIndex 邮件页码
     * @return 邮件页面
     * @throws ContentMissedException 没有获取到内容
     */
    private String getMailListByPageIndex(String sid, int pageIndex) throws ContentMissedException {

        int start = pageIndex * signalPageLimit;
        String uri163 = "https://mail.163.com/js6/s?sid=" + sid + "&func=mbox:listMessages&topNav_mobileIcon_show=1&LeftNavfolder1Click=1&mbox_folder_enter=1";
        String uri126 = "https://mail.126.com/js6/s?sid=" + sid + "&func=mbox:listMessages&topNav_mobileIcon_show=1&LeftNavfolder1Click=1&mbox_folder_enter=1";
        String uriYeah = "https://mail.yeah.net/js6/s?sid=" + sid + "&func=mbox:listMessages&topNav_mobileIcon_show=1&LeftNavfolder1Click=1&mbox_folder_enter=1";
        String uri = MailSwitcher.switchParameter(mailType, uri163, uri126, uriYeah);
        String postData = "var=<?xml version=\"1.0\"?>" +
                "<object>" +
                "<int name=\"fid\">1</int>" +
                "<string name=\"order\">date</string>" +
                "<boolean name=\"desc\">true</boolean>" +
                "<int name=\"limit\">" + signalPageLimit + "</int>" +
                "<int name=\"start\">" + start + "</int>" +
                "<boolean name=\"skipLockedFolders\">false</boolean>" +
                "<string name=\"topFlag\">top</string>" +
                "<boolean name=\"returnTag\">true</boolean>" +
                "<boolean name=\"returnTotal\">true</boolean>" +
                "</object>";
        HttpRequest httpRequest = baseRequestBuilder
                .copy()
                .uri(URI.create(uri))
                .setHeader("Accept", "text/javascript")
                .setHeader("Content-type", "application/x-www-form-urlencoded")
                .method("POST", HttpRequest.BodyPublishers.ofString(postData))
                .build();
        try {
            HttpResponse<byte[]> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray());
            return HttpResponseUtil.getBody(response);
        } catch (IOException | InterruptedException e) {
            throw new ContentMissedException("没有获取到邮件列表:" + e.getMessage());
        }
    }

    /**
     * 解析邮件列表
     *
     * @param mailListPage 邮件列表页面
     * @return 邮件列表
     */
    private List<NetEasyMailEntity> parseMailListPage(String mailListPage) {

        List<NetEasyMailEntity> netEasyMailEntityList = new ArrayList<>();

        //将每条邮件解析成单条
        List<String> mailStringList = RegexSearcher.match(mailListPage, "'id'((?!})[\\s\\S])*(?=})");
        for (String mailString : mailStringList) {
            try {
                NetEasyMailEntity netEasyMailEntity = parseMailString(mailString);
                netEasyMailEntityList.add(netEasyMailEntity);
            } catch (ParseMailException e) {
                String errMsg = e.getMessage();
                if (errMsg.equals(StatusCode.MAIL_PARSE_EXPIRE)) {
                    printlnInfo("半年内邮件解析完成");
                    break;
                } else {
                    printlnErr("解析邮件失败," + errMsg + ":" + mailString);
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return netEasyMailEntityList;
    }

    /**
     * 解析邮件字符串,并获取邮件内容
     *
     * @param mailString 邮件字符串
     * @return 邮件实体
     * @throws ParseMailException 解析失败
     */
    private NetEasyMailEntity parseMailString(String mailString) throws ParseMailException {

        String dateString = RegexSearcher.matchFirst(mailString, "(?<='sentDate':)((?![)],).)*[)](?=,)");
        List<String> dateNumList = RegexSearcher.match(dateString, "\\d{1,4}");
        Date date = new Date();
        try {
            dateString = dateNumList.get(0) + "-" + dateNumList.get(1) + "-" + dateNumList.get(2)
                    + " " + dateNumList.get(3) + ":" + dateNumList.get(4) + ":" + dateNumList.get(5);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            date = simpleDateFormat.parse(dateString);
        } catch (ParseException e) {
            printlnErr("未获取到发送时间戳,默认使用当前时间戳:" + e.getMessage());
        }
        //邮件日期是否在半年之内
        isDuringHalfYear = PublicMethod.isDuringHalfYear(date);
        if (!isDuringHalfYear) {
            throw new ParseMailException(StatusCode.MAIL_PARSE_EXPIRE);
        }
        String subject = RegexSearcher.matchFirst(mailString, "(?<='subject':')((?!').)*");
        //如果主题中不包含账单关键字,抛出异常
        if (!subject.contains(StatusCode.MAIL_CREDIT_KEY_WORD)) {
            throw new ParseMailException(StatusCode.MAIL_PARSE_NOT_CREDIT);
        }
        //如果没有获取到邮件id,抛出异常
        String mailId = RegexSearcher.matchFirst(mailString, "(?<='id':')((?!').)*");
        if (mailId.isEmpty() || mailId.isBlank()) {
            throw new ParseMailException(StatusCode.MAIL_PARSE_NO_ID);
        }
        String senderString = RegexSearcher.matchFirst(mailString, "(?<='from':')((?!').)*");
        //发送人
        String sender = RegexSearcher.matchFirst(senderString, "(?<=\")((?!\").)*(?=\")");
        //发送地址
        String sendAddr = RegexSearcher.matchFirst(senderString, "(?<=<)((?!>).)*(?=>)");
        String recipientString = RegexSearcher.matchFirst(mailString, "(?<='to':')((?!').)*");
        //收件人
        String recipient = RegexSearcher.matchFirst(recipientString, "(?<=\")((?!\").)*(?=\")");
        //收件地址
        String recipientAddr = RegexSearcher.matchFirst(recipientString, "(?<=<)((?!>).)*(?=>)");
        //邮件详情
        String content = getMailContent(mailId);

        return new NetEasyMailEntity(mailId, subject, sender, sendAddr, recipient, recipientAddr, date, content);
    }

    /**
     * 根据邮件id,获取邮件详情
     *
     * @param mailId 邮件id
     * @return 邮件详情
     * @throws MailDetailException 未获取到邮件内容
     */
    private String getMailContent(String mailId) throws MailDetailException {
        String uri163 = "https://mail.163.com/js6/read/readhtml.jsp?mid=" + mailId + "&userType=ud&font=15&color=064977";
        String uri126 = "https://mail.126.com/js6/read/readhtml.jsp?mid=" + mailId + "&userType=browser&font=15&color=138144";
        String uriYeah = "https://mail.yeah.net/js6/read/readhtml.jsp?mid=" + mailId + "&userType=browser&font=15&color=064977";
        String uri = MailSwitcher.switchParameter(mailType, uri163, uri126, uriYeah);

        String detail;
        try {
            detail = PublicMethod.getTextFromUrl(httpClient, baseRequestBuilder, uri);
        } catch (IOException | InterruptedException e) {
            throw new MailDetailException(StatusCode.MAIL_PARSE_NO_CONTENT);
        }

        return detail;
    }
}
